package cn.aitrox.ry.service.anno.service.impl;

import cn.aitrox.ry.bean.RespResult;
import cn.aitrox.ry.common.service.BaseService;
import cn.aitrox.ry.constant.CommonConstant;
import cn.aitrox.ry.enumtype.RespCodeEnum;
import cn.aitrox.ry.service.anno.dao.AnnoComponentResultDao;
import cn.aitrox.ry.service.anno.dao.AnnoLesionResultDao;
import cn.aitrox.ry.service.anno.dao.AnnoLesionResultDetailDao;
import cn.aitrox.ry.service.anno.dao.AnnoLesionResultGroupDao;
import cn.aitrox.ry.service.anno.dao.AnnoResultDao;
import cn.aitrox.ry.service.anno.dao.TaskDao;
import cn.aitrox.ry.service.anno.dao.TaskSeriesDao;
import cn.aitrox.ry.service.anno.dao.UserTaskDao;
import cn.aitrox.ry.service.anno.dto.anno.result.in.AnnoResultSearchInDto;
import cn.aitrox.ry.service.anno.dto.anno.result.out.AnnoLesionResultDetailDto;
import cn.aitrox.ry.service.anno.dto.anno.result.out.AnnoResultDto;
import cn.aitrox.ry.service.anno.dto.anno.result.out.AnnoResultExportResultOutDto;
import cn.aitrox.ry.service.anno.dto.anno.result.out.ComponentResultDto;
import cn.aitrox.ry.service.anno.dto.anno.result.out.DeepzoomUrlOutDto;
import cn.aitrox.ry.service.anno.dto.anno.result.out.LesionResultDto;
import cn.aitrox.ry.service.anno.dto.anno.result.out.LessionResultGroupDto;
import cn.aitrox.ry.service.anno.dto.anno.result.out.SeriesGetResultDto;
import cn.aitrox.ry.service.anno.entity.AnnoComponentResultEntity;
import cn.aitrox.ry.service.anno.entity.AnnoLesionResultDetailEntity;
import cn.aitrox.ry.service.anno.entity.AnnoLesionResultEntity;
import cn.aitrox.ry.service.anno.entity.AnnoLesionResultGroupEntity;
import cn.aitrox.ry.service.anno.entity.AnnoResultEntity;
import cn.aitrox.ry.service.anno.entity.TaskEntity;
import cn.aitrox.ry.service.anno.entity.TaskSeriesEntity;
import cn.aitrox.ry.service.anno.entity.UserTaskEntity;
import cn.aitrox.ry.service.anno.enumtype.anno.AnnoResultStatusEnum;
import cn.aitrox.ry.service.anno.enumtype.anno.LesionResultTypeEnum;
import cn.aitrox.ry.service.anno.enumtype.task.TaskStatusEnum;
import cn.aitrox.ry.service.anno.mq.produser.Mask2niiProduser;
import cn.aitrox.ry.service.anno.po.anno.result.AnnoResultQueryParamPo;
import cn.aitrox.ry.service.anno.service.AnnoResultService;
import cn.aitrox.ry.service.anno.service.FileHandlerInfoService;
import cn.aitrox.ry.util.ArrayUtil;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class AnnoResultServiceImpl extends BaseService implements AnnoResultService {

    @Autowired
    private AnnoResultDao annoResultDao;

    @Autowired
    private AnnoLesionResultDao lesionResultDao;

    @Autowired
    private AnnoComponentResultDao componentResultDao;

    @Autowired
    private UserTaskDao userTaskDao;

    @Autowired
    private TaskDao taskDao;

    @Autowired
    private TaskSeriesDao taskSeriesDao;

    @Autowired
    private AnnoLesionResultDetailDao annoLesionResultDetailDao;

    @Autowired
    private FileHandlerInfoService fileHandlerInfoService;

    @Autowired
    private Mask2niiProduser mask2niiProduser;

    @Autowired
    private AnnoLesionResultGroupDao annoLesionResultGroupDao;

    @Transactional
    @Override
    public RespResult<SeriesGetResultDto> seriesGet(Integer userId, Integer taskId) {
        // TODO 暂不考虑辅助序列参与标注结果

        UserTaskEntity userTaskEntity = userTaskDao.findByUserIdTaskId(userId, taskId);
        if (null == userTaskEntity) {
            return RespResult.error(RespCodeEnum.NO_PERMISSION);
        }

        TaskEntity task = taskDao.findById(taskId);
        if (null == task) {
            return RespResult.error(RespCodeEnum.PARAM_ERROR);
        }

        // 查询用户正在标注中的结果
        List<AnnoResultEntity> arListInProcess = annoResultDao.findByTaskIdUserIdStatus(taskId, userId, AnnoResultStatusEnum.PROCESSING.getStatus());

        if (!ArrayUtil.isEmpty(arListInProcess)) {
            List<AnnoResultDto> arListOut = arListInProcess.stream().map(x -> {
                return new AnnoResultDto(x.getId(), x.getSeriesUid(), x.getStatus(), null, null, null, null, null, null);
            }).collect(Collectors.toList());

            return RespResult.ok(new SeriesGetResultDto(arListInProcess.get(0).getGroupId(), arListOut));
        }

        String groupId = this.triggerUserTaskAndTaskComplete(userTaskEntity, task);
        if (null == groupId) {
            // 领取不到了，触发 用户任务、任务的状态完成
            return RespResult.ok(new SeriesGetResultDto());
        }

        Date now = new Date();

        // 查询标注结果表是否有未被领取的预处理/导入结果
        List<AnnoResultEntity> getableArList = annoResultDao.findByTaskIdGroupIdUserIdNotGtZero(taskId, groupId);
        if (!ArrayUtil.isEmpty(getableArList)) {
            List<Integer> arIdList = getableArList.stream().map(AnnoResultEntity::getId).collect(Collectors.toList());

            AnnoResultEntity update = new AnnoResultEntity();
            update.setUserId(userId);
            update.setStatus(AnnoResultStatusEnum.PROCESSING.getStatus());
            annoResultDao.updateByIdsSelective(update, arIdList.toArray());

            List<AnnoResultDto> arListOut = getableArList.stream().map(x -> {
                return new AnnoResultDto(x.getId(), x.getSeriesUid(), x.getStatus(), null, null, null, null, null, null);
            }).collect(Collectors.toList());

            // 触发更新用户任务的开始标注时间
            this.triggerUserTaskBeginTimeUpdate(userTaskEntity.getId(), now);

            return RespResult.ok(new SeriesGetResultDto(getableArList.get(0).getGroupId(), arListOut));
        }


        List<TaskSeriesEntity> tsList = taskSeriesDao.findByTaskIdGroupId(taskId, groupId);
        List<String> seriesUidList = tsList.stream().map(TaskSeriesEntity::getSeriesUid).collect(Collectors.toList());

        // 创建标注结果
        List<AnnoResultEntity> annoResultList2Save = this.createAnnoResult(taskId, userId, groupId, seriesUidList, now);

        annoResultDao.batchSave(annoResultList2Save);

        List<AnnoResultEntity> annoResultListInDB = annoResultDao.findByTaskIdUserIdGroupId(taskId, userId, groupId);

        List<AnnoResultDto> arListOut = annoResultListInDB.stream().map(x -> {
            return new AnnoResultDto(x.getId(), x.getSeriesUid(), x.getStatus(), null, null, null, null, null, null);
        }).collect(Collectors.toList());


        // 触发更新用户任务的开始标注时间
        this.triggerUserTaskBeginTimeUpdate(userTaskEntity.getId(), now);

        return RespResult.ok(new SeriesGetResultDto(groupId, arListOut));
    }

    @Transactional
    @Override
    public void yay(Integer arId, Integer yay) {
        AnnoResultEntity update = new AnnoResultEntity();
        update.setId(arId);
        update.setYay(yay);
        annoResultDao.updateByIdSelective(update);
    }

    @Transactional
    @Override
    public RespResult submit(Integer userId, List<AnnoResultEntity> arList, Integer status, String discardCode, String discardRemark) {
        AnnoResultStatusEnum statusEnum = AnnoResultStatusEnum.getEnumByStatus(status);
        if (AnnoResultStatusEnum.DISCARD == statusEnum) {
            discardCode = CommonConstant.EMPTY;
            discardRemark = CommonConstant.EMPTY;
        }

        // TODO 校验参数
        List<Integer> arIdList = arList.stream().map(AnnoResultEntity::getId).collect(Collectors.toList());

        // 更新Ar、触发用户任务状态更新、触发任务状态更新
        Integer taskId = arList.get(0).getTaskId();
        TaskEntity task = taskDao.findById(taskId);
        UserTaskEntity userTask = userTaskDao.findByUserIdTaskId(userId, taskId);

        AnnoResultEntity update = new AnnoResultEntity();
        update.setStatus(status);
        update.setSubmitTime(new Date());
        update.setDiscardCode(discardCode);
        update.setDiscardRemark(discardRemark);
        annoResultDao.updateByIdsSelective(update, arIdList.toArray());

        List<AnnoLesionResultEntity> alrList = lesionResultDao.findByArIds(arIdList);
        List<Integer> maskAlrIds = alrList.stream().filter(x -> LesionResultTypeEnum._isMask(x.getResultType())).map(AnnoLesionResultEntity::getId).collect(Collectors.toList());
        if (!ArrayUtil.isEmpty(maskAlrIds)) {
            // 清空mask地址
            lesionResultDao.clearMaskFilePath(maskAlrIds);

            // 发送mq进行nii转换
            for (Integer alrId : maskAlrIds) {
                mask2niiProduser.send(alrId);
            }
        }

        this.triggerUserTaskAndTaskComplete(userTask, task);
        return RespResult.ok();
    }

    @Override
    public RespResult<AnnoResultDto> read(Integer arId) {
        AnnoResultEntity annoResultEntity = annoResultDao.findById(arId);
        if (null == annoResultEntity) {
            return RespResult.error(RespCodeEnum.PARAM_ERROR);
        }
        List<AnnoLesionResultEntity> alrList = lesionResultDao.findByArId(arId);

        List<AnnoComponentResultEntity> acrList = componentResultDao.findByArIdAlrId(arId, null);

        List<Integer> alrIdList = alrList.stream().map(AnnoLesionResultEntity::getId).distinct().collect(Collectors.toList());

        Map<Integer, List<AnnoLesionResultDetailEntity>> alrdMap = new HashMap<>();
        if (!ArrayUtil.isEmpty(alrIdList)) {
            List<AnnoLesionResultDetailEntity> alrdList = annoLesionResultDetailDao.findByAlrIds(alrIdList);
            alrdMap = alrdList.stream().collect(Collectors.groupingBy(AnnoLesionResultDetailEntity::getAlrId));
        }

        List<AnnoLesionResultGroupEntity> groupList = annoLesionResultGroupDao.findByCondition(new AnnoLesionResultGroupEntity(null, null, arId, null, null, null), null);

        Map<Integer, List<AnnoLesionResultDetailEntity>> finalAlrdMap = alrdMap;

        List<ComponentResultDto> seriesCompResultList = acrList.stream().filter(acr -> acr.getAlrId() == 0).map(acr -> {
            return new ComponentResultDto(acr.getId(), acr.getComponentId(), acr.getResult());
        }).collect(Collectors.toList());

        List<LesionResultDto> lesionResultList = alrList.stream().map(alr -> {
            List<ComponentResultDto> imageCompResultList = acrList.stream().filter(acr -> acr.getAlrId().equals(alr.getId())).map(acr -> {
                return new ComponentResultDto(acr.getId(), acr.getComponentId(), acr.getResult());
            }).collect(Collectors.toList());

            Map<String, List<AnnoLesionResultDetailDto>> resultDetailMap = null;
            List<AnnoLesionResultDetailEntity> alrdListByAlrId = finalAlrdMap.get(alr.getId());
            if (null != alrdListByAlrId) {
                resultDetailMap = alrdListByAlrId.stream().map(alrd -> {
                    return new AnnoLesionResultDetailDto(alrd.getId(), alrd.getGroupId(), alrd.getTool(), alrd.getColor(), alrd.getLayer(), alrd.getResult(), alrd.getResultType());
                }).collect(Collectors.groupingBy(AnnoLesionResultDetailDto::getGroupId));
            }
            return new LesionResultDto(alr.getId(), alr.getLesion(), alr.getColor(), alr.getTool(), alr.getResult(), alr.getResultType(), resultDetailMap, imageCompResultList, alr.getGroupId(), alr.getAlg());
        }).collect(Collectors.toList());

        List<LessionResultGroupDto> groupDtoList = groupList.stream().map(x -> new LessionResultGroupDto(x.getId(), x.getTaskId(), x.getArId(), x.getType(), x.getInfo(), x.getAlgProcessed(), x.getCreateTime(), x.getUpdateTime())).collect(Collectors.toList());

        AnnoResultDto annoResult = new AnnoResultDto(arId, annoResultEntity.getSeriesUid(), annoResultEntity.getStatus(), annoResultEntity.getYay(), annoResultEntity.getDiscardCode(), annoResultEntity.getDiscardRemark(), seriesCompResultList, lesionResultList, groupDtoList);

        return RespResult.ok(annoResult);
    }

    @Override
    public RespResult<AnnoResultEntity> checkAr(Integer userId, Integer arId) {
        AnnoResultEntity annoResult = annoResultDao.findById(arId);
        if (null == annoResult) {
            return RespResult.error(RespCodeEnum.PARAM_ERROR);
        }
        if (!annoResult.getUserId().equals(userId)) {
            return RespResult.error(RespCodeEnum.NO_PERMISSION);
        }
        return RespResult.ok(annoResult);
    }

    @Override
    public List<AnnoResultEntity> getAnnoResultEntitys(List<Integer> arIdList) {
        return annoResultDao.findByIds(arIdList.toArray(), null);
    }

    @Override
    public List<Integer> getUserIds(Integer taskId) {
        return annoResultDao.findUserIdsByTaskId(taskId);
    }

    @Override
    public PageInfo<AnnoResultEntity> search(AnnoResultSearchInDto input) {
        AnnoResultQueryParamPo annoResultQueryParamPo = new AnnoResultQueryParamPo(input.getSeriesUids());
        return annoResultDao.searchByParmas(input.getPage(), input.getPageSize(), annoResultQueryParamPo);
    }

    @Override
    public AnnoResultExportResultOutDto export(List<Integer> taskIds) {
        if (ArrayUtil.isEmpty(taskIds)) {
            return new AnnoResultExportResultOutDto();
        }
        List<AnnoResultEntity> annoResultList = annoResultDao.findByTaskIdsStatus(taskIds, AnnoResultStatusEnum.SUBMIT.getStatus(), AnnoResultStatusEnum.DISCARD.getStatus());
        if (ArrayUtil.isEmpty(annoResultList)) {
            return new AnnoResultExportResultOutDto();
        }

        List<AnnoLesionResultGroupEntity> groupList = annoLesionResultGroupDao.findByTaskIds(taskIds);

        List<Integer> arIdList = annoResultList.stream().map(AnnoResultEntity::getId).collect(Collectors.toList());

        List<AnnoLesionResultEntity> alrList = lesionResultDao.findByArIds(arIdList);

        List<AnnoComponentResultEntity> acrList = componentResultDao.findByArIds(arIdList);

        if (!ArrayUtil.isEmpty(alrList)) {
            List<Integer> triggerNiialrIdList = alrList.stream().filter(x -> LesionResultTypeEnum._isMask(x.getResultType()) && StringUtils.isEmpty(x.getMaskFilePath())).map(AnnoLesionResultEntity::getId).collect(Collectors.toList());
            // 发送mq进行nii转换
            for (Integer alrId : triggerNiialrIdList) {
                mask2niiProduser.send(alrId);
            }
        }

        return new AnnoResultExportResultOutDto(annoResultList, alrList, acrList, groupList);
    }

    @Override
    public RespResult<DeepzoomUrlOutDto> deepzoomUrl(Integer taskId, String seriesUid) {
        RespResult<String> respResult = fileHandlerInfoService.getDeepzoomUrl(taskId, seriesUid, true);
        if (!respResult._isOk()) {
            return RespResult.secByError(respResult.getCode(), respResult.getMsg());
        }
        return RespResult.ok(new DeepzoomUrlOutDto(respResult.getData()));
    }

    private List<AnnoResultEntity> createAnnoResult(Integer taskId, Integer userId, String groupId, List<String> seriesUidList, Date now) {
        return seriesUidList.stream().map(seriesUid -> {
            AnnoResultEntity annoResultEntity = new AnnoResultEntity();
            annoResultEntity.setTaskId(taskId);
            annoResultEntity.setUserId(userId);
            annoResultEntity.setSeriesUid(seriesUid);
            annoResultEntity.setGroupId(groupId);
            annoResultEntity.setStatus(AnnoResultStatusEnum.PROCESSING.getStatus());
            annoResultEntity.setBeginTime(now);
            return annoResultEntity;
        }).collect(Collectors.toList());
    }

    private String triggerUserTaskAndTaskComplete(UserTaskEntity userTask, TaskEntity task) {
        String groupId = annoResultDao.findGetableGroupId(task.getId(), userTask.getUserId(), task.getCrossNum());
        if (null != groupId) {
            return groupId;
        }
        UserTaskEntity update = new UserTaskEntity();
        update.setId(userTask.getId());
        update.setStatus(TaskStatusEnum.COMPLETE.getStatus());
        update.setEndTime(new Date());
        userTaskDao.updateByIdSelective(update);
        // 查询任务序列是否已全部领取完
        Integer annoGroupCount = annoResultDao.findGroupIdCountByTaskIdStatusUserIdGtZero(userTask.getTaskId(), AnnoResultStatusEnum.SUBMIT.getStatus(), AnnoResultStatusEnum.DISCARD.getStatus());
        Integer totalGroupCount = taskSeriesDao.findGroupIdCountByTaskId(userTask.getTaskId());
        if (annoGroupCount >= (totalGroupCount * task.getCrossNum())) {
            taskDao.updateStatusById(userTask.getTaskId(), TaskStatusEnum.COMPLETE.getStatus());
        }
        return null;
    }

    private void triggerUserTaskBeginTimeUpdate(Integer userTaskId, Date now) {
        UserTaskEntity update = new UserTaskEntity();
        update.setId(userTaskId);
        update.setBeginTime(now);
        userTaskDao.updateByIdSelective(update);
    }

}
